package craky.keeper.remote;

import java.util.List;

import com.caucho.hessian.server.HessianServlet;

import craky.keeper.bean.Category;
import craky.keeper.bean.Income;
import craky.keeper.bean.KeeperObject;
import craky.keeper.bean.Pay;
import craky.keeper.bean.ResultSet;
import craky.keeper.bean.User;
import craky.keeper.dao.CategoryDAO;
import craky.keeper.dao.IncomeDAO;
import craky.keeper.dao.PayDAO;
import craky.keeper.dao.UserDAO;
import craky.keeper.sql.Criterion;
import craky.keeper.sql.Restrictions;

public class KeeperDBRemote extends HessianServlet implements RemoteInterface
{
    private static final long serialVersionUID = -4652362786206117804L;

    private long lastPayUpdateTime, lastIncomeUpdateTime;

    public KeeperDBRemote()
    {
        long current = System.currentTimeMillis();
        this.lastPayUpdateTime = current;
        this.lastIncomeUpdateTime = current;
    }

    @Override
    public ResultSet login(String name, String password)
    {
        ResultSet resultSet = new ResultSet();
        User user = UserDAO.login(name, password);
        resultSet.setUser(user);

        if(user != null && user.getPassword() != null)
        {
            resultSet.setPayCategoryList(CategoryDAO.getCategorys(true));

            if(user.getPurview() < User.VISITOR)
            {
                resultSet.setIncomeCategoryList(CategoryDAO.getCategorys(false));
            }
        }

        return resultSet;
    }

    @Override
    public boolean isFirstUse()
    {
        return UserDAO.isFirst();
    }

    @Override
    public List<User> getUsers(Criterion...criterions)
    {
        return UserDAO.getUsers(criterions);
    }

    @Override
    public User addUser(User user)
    {
        UserDAO.addUser(user);
        return user;
    }

    @Override
    public void deleteUser(User user)
    {
        UserDAO.deleteUser(user);
    }

    @Override
    public User updateUser(User user)
    {
        UserDAO.updateUser(user);
        return user;
    }

    @Override
    public ResultSet getPartiallyPays(long cachedUpdateTime, int resultCount, Criterion...criterions)
    {
        ResultSet resultSet = new ResultSet();
        long lastUpdateTime = this.getLastUpdateTime(true);

        if(cachedUpdateTime <= 0 || cachedUpdateTime == lastUpdateTime)
        {
            resultSet.setPayCategoryList(CategoryDAO.getCategorys(true));
            resultSet.setPayList(PayDAO.getPays(resultCount, criterions));
        }

        resultSet.setLastUpdateTime(lastUpdateTime);
        return resultSet;
    }

    @Override
    public List<Pay> getPays(Criterion...criterions)
    {
        return PayDAO.getPays(0, criterions);
    }

    @Override
    public ResultSet addPay(Pay pay, Category category)
    {
        PayDAO.addPay(pay, category);
        return createResultSet(pay);
    }

    @Override
    public ResultSet deletePay(Pay pay, Category category)
    {
        PayDAO.deletePay(pay, category);
        return createResultSet(pay);
    }

    @Override
    public ResultSet updatePay(Pay pay, Category oldCategory, Category newCategory)
    {
        PayDAO.updatePay(pay);
        updateCategoryAfterItemModified(true, oldCategory, newCategory);
        return createResultSet(pay);
    }

    @Override
    public ResultSet getPartiallyIncomes(long cachedUpdateTime, int resultCount, Criterion...criterions)
    {
        ResultSet resultSet = new ResultSet();
        long lastUpdateTime = this.getLastUpdateTime(false);

        if(cachedUpdateTime <= 0 || cachedUpdateTime == lastUpdateTime)
        {
            resultSet.setIncomeCategoryList(CategoryDAO.getCategorys(false));
            resultSet.setIncomeList(IncomeDAO.getIncomes(resultCount, criterions));
        }

        resultSet.setLastUpdateTime(lastUpdateTime);
        return resultSet;
    }

    @Override
    public List<Income> getIncomes(Criterion...criterions)
    {
        return IncomeDAO.getIncomes(0, criterions);
    }

    @Override
    public ResultSet addIncome(Income income, Category category)
    {
        IncomeDAO.addIncome(income, category);
        return createResultSet(income);
    }

    @Override
    public ResultSet deleteIncome(Income income, Category category)
    {
        IncomeDAO.deleteIncome(income, category);
        return createResultSet(income);
    }

    @Override
    public ResultSet updateIncome(Income income, Category oldCategory, Category newCategory)
    {
        IncomeDAO.updateIncome(income);
        updateCategoryAfterItemModified(false, oldCategory, newCategory);
        return createResultSet(income);
    }

    @Override
    public ResultSet getCategorys(boolean includeIncome)
    {
        ResultSet resultSet = new ResultSet();
        resultSet.setPayCategoryList(CategoryDAO.getCategorys(true));

        if(includeIncome)
        {
            resultSet.setIncomeCategoryList(CategoryDAO.getCategorys(false));
        }

        return resultSet;
    }

    @Override
    public Category addCategory(Category category)
    {
        CategoryDAO.addCategory(category);
        return category;
    }

    @Override
    public Category deleteCategory(Category category, boolean isPay)
    {
        List<Category> categoryList = CategoryDAO.getCategorys(isPay, Restrictions.eq("name", category.getName()));

        if(categoryList != null && !categoryList.isEmpty())
        {
            category = categoryList.get(0);

            if(category.getCount() <= 0)
            {
                CategoryDAO.deleteCategory(category);
            }
        }
        else
        {
            category.setCount(0);
        }

        return category;
    }

    @Override
    public boolean existCategory(boolean isPay, String name)
    {
        return !CategoryDAO.getCategorys(isPay, Restrictions.eq("name", name)).isEmpty();
    }

    private void updateCategoryAfterItemModified(boolean isPay, Category oldCategory, Category newCategory)
    {
        if(oldCategory != null && newCategory != null)
        {
            List<Category> oldCategoryList = CategoryDAO.getCategorys(isPay, Restrictions.eq("name", oldCategory.getName()));
            List<Category> newCategoryList = CategoryDAO.getCategorys(isPay, Restrictions.eq("name", newCategory.getName()));

            if(oldCategoryList != null && !oldCategoryList.isEmpty())
            {
                CategoryDAO.afterRemoveItem(oldCategoryList.get(0));
            }

            if(newCategoryList != null && !newCategoryList.isEmpty())
            {
                CategoryDAO.afterInsertItem(newCategoryList.get(0));
            }
        }
    }

    private ResultSet createResultSet(KeeperObject keeperObject)
    {
        ResultSet resultSet = new ResultSet();
        boolean isPay = keeperObject instanceof Pay;

        if(isPay)
        {
            resultSet.setPay((Pay)keeperObject);
        }
        else
        {
            resultSet.setIncome((Income)keeperObject);
        }

        resultSet.setOldUpdateTime(this.getLastUpdateTime(isPay));
        this.updateLastTime(isPay);
        resultSet.setLastUpdateTime(this.getLastUpdateTime(isPay));
        return resultSet;
    }

    private long updateLastTime(boolean isPay)
    {
        long current = System.currentTimeMillis();

        if(isPay)
        {
            this.lastPayUpdateTime = current;
        }
        else
        {
            this.lastIncomeUpdateTime = current;
        }

        return current;
    }

    private long getLastUpdateTime(boolean isPay)
    {
        return isPay? lastPayUpdateTime: lastIncomeUpdateTime;
    }
}